﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace PsiIndexer
{
  public class PsiIndexer
  {
    private Dictionary<Guid, PsiChatInfo> chatStore;
    private Dictionary<string, PsiConversation> conversationStore;
    private Action<string> infoAction;

    private PsiIndexer()
    {
    }

    public LuceneIndexer LuceneIndexer { get; private set; }
    public event EventHandler<StausInfoArgs> OnStatusInfo;

    public static PsiIndexer Create(string historyDirectory, bool withUpdate, bool persistIndex, Action<string> infoAction)
    {
      var pi = new PsiIndexer();
      pi.infoAction = infoAction;
      pi.OnStatusInfo += new EventHandler<StausInfoArgs>(pi_OnStatusInfo);

      Stopwatch sw = Stopwatch.StartNew();
      Console.Write("read data...");
      PublishStatusInfo(pi, "read data...");

      FileSystemWatcher fsw = null;
      if (withUpdate) {
        fsw = new FileSystemWatcher();
        fsw.Path = historyDirectory;
        fsw.Filter = "*.history";
        fsw.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName |
                           NotifyFilters.DirectoryName;
        fsw.Changed += pi.fswChanged;
      }

      pi.conversationStore = new Dictionary<string, PsiConversation>();
      foreach (string file in Directory.GetFiles(historyDirectory, "*.history")) {
        PsiConversation pc = PsiConversation.Create(file);
        if (pc != null) {
          pi.conversationStore.Add(file, pc);
          PublishStatusInfo(pi, string.Format("read {0}", new FileInfo(pc.OriginFile).Name));
        }
      }
      Console.WriteLine("took {0}ms for {1} files", sw.ElapsedMilliseconds, pi.conversationStore.Count);
      sw.Restart();

      // add to localcache
      pi.chatStore = pi.conversationStore.Values.SelectMany(pc => pc.Chats).ToDictionary(pci => pci.ID, pci => pci);

      pi.LuceneIndexer = new LuceneIndexer();
      Console.Write("index data...");
      PublishStatusInfo(pi, "index data...");
      pi.LuceneIndexer.Index(pi.conversationStore.Values);
      sw.Stop();
      Console.WriteLine("took {0}ms", sw.ElapsedMilliseconds);
      Console.Write("optimize index...");
      PublishStatusInfo(pi, "optimizes index...");
      sw.Restart();
      pi.LuceneIndexer.OptimizeAndClose();
      Console.WriteLine("took {0}ms", sw.ElapsedMilliseconds);
      if (withUpdate) {
        fsw.EnableRaisingEvents = true;
      }
      PublishStatusInfo(pi, "finished...");
      return pi;
    }

    private static void pi_OnStatusInfo(object sender, StausInfoArgs e)
    {
      PsiIndexer pi = sender as PsiIndexer;
      if (pi != null && pi.infoAction != null) {
        pi.infoAction(e.Info);
      }
    }

    private static void PublishStatusInfo(PsiIndexer pi, string info)
    {
      EventHandler<StausInfoArgs> tmp = pi.OnStatusInfo;
      if (tmp != null) {
        tmp(pi, new StausInfoArgs {Info = info});
      }
    }

    public SearchResult Search(string txt)
    {
      return this.LuceneIndexer.Search(txt, 10);
    }

    private void fswChanged(object sender, FileSystemEventArgs e)
    {
      PsiConversation conversation;
      if (this.conversationStore.TryGetValue(e.FullPath, out conversation)) {
        conversation.Update();
      }
    }

    public PsiChatInfo GetChat(string id)
    {
      Guid guid = new Guid(id);
      PsiChatInfo chat;
      if (this.chatStore.TryGetValue(guid, out chat)) {
        return chat;
      }
      return null;
    }
  }

  public class StausInfoArgs : EventArgs
  {
    public string Info { get; set; }
  }
}